package com.isesol.arch.common.utils;

import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

/**
 * 日期工具类
 *
 */
public class DateUtil {

    /**
     * 日期排序类型-升序
     */
    public final static int DATE_ORDER_ASC = 0;

    /**
     * 日期排序类型-降序
     */
    public final static int DATE_ORDER_DESC = 1;

    /**
     * 转换字符串为日期
     *
     * @throws ParseException
     * @dateValue 日期字符串
     * @dateType 日期字符串格式
     */
    public static Date parse(String dateValue, String dateType) throws ParseException {
        Assert.notNull(dateValue, "parameter dataValue can not be null.");
        Assert.notNull(dateType, "parameter dateType can not be null.");

        SimpleDateFormat sfdate = new SimpleDateFormat(dateType);
        return sfdate.parse(dateValue);
    }

    /**
     * 用字符串获得java.sql.Date日期
     *
     * @throws ParseException
     * @dateValue 日期字符串
     * @dateType 日期字符串格式
     */
    public static java.sql.Date parseSqlDate(String dateValue, String dateType) throws ParseException {
        Date date = parse(dateValue, dateType);
        if (date == null) {
            return null;
        }
        return new java.sql.Date(date.getTime());
    }

    /**
     * 将日期加上某些天或减去天数
     *
     * @param date 待处理日期
     * @param to   加减的天数
     * @return 处理后的日期
     */
    public static Date dateAdd(String date, int to) {
        Assert.notNull(date, "parameter date can not be null");
        Assert.notNull(to, "parameter to can not be null");

        java.sql.Date d = java.sql.Date.valueOf(date);
        return dateAdd(d, to);
    }

    /**
     * 将日期加上某些天或减去天数)返回字符串
     *
     * @param date 待处理日期
     * @param to   加减的天数
     * @return 日期
     */
    public static java.sql.Date dateAdd(java.sql.Date date, int to) {
        Calendar strDate = Calendar.getInstance();
        strDate.setTime(date);
        strDate.add(Calendar.DATE, to); // 日期减 如果不够减会将月变动
        return new java.sql.Date(strDate.getTime().getTime());
    }

    /**
     * 格式化日期对象为字符串
     *
     * @dateValue 日期对象，可以是java.util.Date和java.sql.Date
     * @dateType 格式化的类型, date和datetime
     */
    public static String format(java.util.Date dateValue, String dateType) {
        if (dateValue == null) {
            return StringUtils.EMPTY;
        }
        if (dateValue instanceof java.util.Date) {
            SimpleDateFormat sfdate = new SimpleDateFormat(dateType);
            return sfdate.format(dateValue);
        } else {
            throw new IllegalArgumentException("不是日期类型：" + dateValue);
        }
    }

    /**
     * 转化成年月日期
     *
     * @param sDate         字符型日期：2009-02-02
     * @param delimeterChar 分割符号比如 / -
     * @return 年月日期 :2009年02月02日
     */
    public static String chDateChange(String sDate, String delimeterChar) {
        String tmpArr[] = sDate.split(delimeterChar);
        tmpArr[0] = tmpArr[0] + "年";
        tmpArr[1] = tmpArr[1] + "月";
        tmpArr[2] = tmpArr[2] + "日";
        return tmpArr[0] + tmpArr[1] + tmpArr[2];
    }

    /**
     * 得到系统日期
     *
     * @return YYYY-MM-DD
     */
    public static String getSysdate() {
        java.sql.Timestamp timeNow = new java.sql.Timestamp(System.currentTimeMillis());
        return format(timeNow, DateFormatType.FORMAT_DATE);
    }

    /**
     * 得到系统日期
     *
     * @return YYYY-MM-DD
     */
    public static String getSysdate(String formatType) {
        java.sql.Timestamp timeNow = new java.sql.Timestamp(System.currentTimeMillis());
        return format(timeNow, formatType);
    }

    /**
     * 得到某天是周几
     *
     * @param strDay
     * @return 周几
     * @throws ParseException
     */
    public static int getWeekDay(String strDay) throws ParseException {
        Date date = parse(strDay, DateFormatType.FORMAT_DATE);
        return getWeekDay(date);
    }

    /**
     * 得到某天是周几
     *
     * @param date
     * @return 周几
     */
    public static int getWeekDay(Date date) {
        Date day = DateUtil.dateAdd(format(date, DateFormatType.FORMAT_DATE), -1);
        Calendar strDate = Calendar.getInstance();
        strDate.setTime(day);
        int meStrDate = strDate.get(Calendar.DAY_OF_WEEK);
        return meStrDate;
    }

    /**
     * 取得两个日期段的日期间隔
     *
     * @param t1 时间1
     * @param t2 时间2
     * @return t2 与t1的间隔天数
     * @throws ParseException 如果输入的日期格式不是0000-00-00 格式抛出异常
     */
    public static int getBetweenDays(String t1, String t2) throws ParseException {
        return getBetweenDays(t1, t2, true);
    }

    /**
     * 取得两个日期段的日期间隔
     *
     * @param t1       时间1
     * @param t2       时间2
     * @param swapDate 当日期1小于日期2时是否交换两个日期值
     * @return t2 与t1的间隔天数
     * @throws ParseException 如果输入的日期格式不是0000-00-00 格式抛出异常
     * @author color
     */
    public static int getBetweenDays(String t1, String t2, boolean swapDate) throws ParseException {
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        int betweenDays = 0;
        Date d1 = format.parse(t1);
        Date d2 = format.parse(t2);
        betweenDays = getBetweenDays(d1, d2, swapDate);
        return betweenDays;
    }

    /**
     * 取得两个日期段的日期间隔
     *
     * @param d1       日期1
     * @param d2       日期2
     * @param swapDate 当日期1小于日期2时是否交换两个日期值
     * @return t2 与t1的间隔天数，如果没有开启交换日期并且d1大于d2时返回负数
     */
    public static int getBetweenDays(Date d1, Date d2, boolean swapDate) {
        if (d1 == null || d2 == null) {
            return -1;
        }
        int betweenDays;
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(d1);
        c2.setTime(d2);
        if (swapDate) {
            // 保证第二个时间一定大于第一个时间
            if (c1.after(c2)) {
                c2.setTime(d1);
                c1.setTime(d2);
            }
        }
        int betweenYears = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
        betweenDays = c2.get(Calendar.DAY_OF_YEAR) - c1.get(Calendar.DAY_OF_YEAR);
        for (int i = 0; i < betweenYears; i++) {
            c1.set(Calendar.YEAR, (c1.get(Calendar.YEAR) + 1));
            betweenDays += c1.getMaximum(Calendar.DAY_OF_YEAR);
        }
        return betweenDays;
    }

    /**
     * 判断指定日期是否在一个日期范围内
     *
     * @param fromDate 范围开始日期
     * @param toDate   范围结束日期
     * @param testDate 测试日期
     * @return 在范围内true, 否则false
     */
    public static boolean betweenDays(java.sql.Date fromDate, java.sql.Date toDate, java.sql.Date testDate) {
        if (fromDate == null || toDate == null || testDate == null) {
            return false;
        }

        //1、 交换开始和结束日期
        if (fromDate.getTime() > toDate.getTime()) {
            java.sql.Date tempDate = fromDate;
            fromDate = toDate;
            toDate = tempDate;
        }

        //2、缩小范围
        long testDateTime = testDate.getTime();
        if ((testDateTime > fromDate.getTime() && testDateTime > toDate.getTime()) || testDateTime < fromDate.getTime()
                && testDateTime < toDate.getTime()) {
            return false;
        }

        return true;
    }

    /**
     * 得到指定年、月的最后一天
     *
     * @param year  年
     * @param month 月
     * @return 本年月的最后一天，如果2009,10，返回结果：2009-10-31
     */
    public static String getLastDate(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month);
        cal.set(Calendar.DAY_OF_MONTH, 1);
        // 某年某月的最后一天
        int lastDate = cal.getActualMaximum(Calendar.DATE);
        cal.set(Calendar.DATE, lastDate);
        return format(cal.getTime(), DateFormatType.FORMAT_DATE);
    }

    /**
     * 判断两个日期是否为同一天
     *
     * @param d1 日期一
     * @param d2 日期二
     * @return 同一天true，不是同一天false
     */
    public static boolean isSameDate(Date d1, Date d2) {

        Assert.notNull(d1, "parameter d1 can not be null.");
        Assert.notNull(d2, "parameter d2 can not be null.");

        boolean result = false;
        Calendar c1 = Calendar.getInstance();
        c1.setTime(d1);

        Calendar c2 = Calendar.getInstance();
        c2.setTime(d2);

        if (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) && c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH)
                && c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH)) {
            result = true;
        }
        return result;
    }

    /**
     * 获取当前系统时间，24小时制
     *
     * @return 当前系统时间
     */
    public static Time getSystemTime() {
        Calendar c1 = Calendar.getInstance();
        int hour = c1.get(Calendar.HOUR_OF_DAY);
        int minute = c1.get(Calendar.MINUTE);
        int second = c1.get(Calendar.SECOND);
        Time systemTime = Time.valueOf(hour + ":" + minute + ":" + second);
        return systemTime;
    }

    /**
     * 是否为周末，周六、周日均代表周末
     *
     * @param strDate 日期
     * @return true|false
     * @throws ParseException
     */
    public static boolean isWeekend(String strDate) throws ParseException {
        int weekDay = getWeekDay(strDate);
        if (weekDay == 6 || weekDay == 7) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 是否为周末
     *
     * @param date 日期
     * @return true|false
     * @throws ParseException
     */
    public static boolean isWeekend(Date date) throws ParseException {
        return isWeekend(format(date, DateFormatType.FORMAT_DATE));
    }

    /**
     * 是否为法定节假日
     *
     * @param strDate
     * @return true|false
     */
    public static boolean isHoliday(String strDate) {
        return false;
    }

    /**
     * 日期排序
     *
     * @param dates     日期列表
     * @param orderType 排序类型
     *                  <br/>{@link #DATE_ORDER_ASC}<br/>
     *                  {@link #DATE_ORDER_DESC}
     * @return 排序结果
     */
    public static List<? extends java.util.Date> orderDate(List<? extends java.util.Date> dates, int orderType) {
        DateComparator comp = new DateComparator(orderType);
        Collections.sort(dates, comp);
        return dates;
    }

    /**
     * 日期分组<br/>
     * 能够对指定日期列表按照连续性分组<br/>
     * 例如：[2010-01-15, 2010-01-16, 2010-01-17, 2010-01-20, 2010-01-21, 2010-01-25]<br/>
     * 分组结果为：<br/>
     * <ul>
     * <li>[2010-01-15, 2010-01-16, 2010-01-17]</li>
     * <li>[2010-01-20, 2010-01-21]</li>
     * <li>[2010-01-25]</li>
     * </ul>
     *
     * @param dates 日期对象
     * @return 连续性分组结果
     */
    public static List<List<? extends java.util.Date>> groupDates(List<? extends java.util.Date> dates) {
        List<List<? extends java.util.Date>> result = new ArrayList<List<? extends java.util.Date>>();

        // 按照升序排序
        orderDate(dates, DateUtil.DATE_ORDER_ASC);

        // 临时结果
        List<Date> tempDates = null;

        // 上一组最后一个日期
        Date lastDate = null;

        // 当前读取日期
        Date cdate = null;
        for (int i = 0; i < dates.size(); i++) {
            cdate = dates.get(i);

            // 第一次增加
            if (tempDates == null) {
                tempDates = new ArrayList<Date>();
                tempDates.add(cdate);
                result.add(tempDates);
            } else {
                /**
                 * 差距为1是继续在原有的列表中添加，大于1就是用新的列表
                 */
                lastDate = tempDates.get(tempDates.size() - 1);
                int days = getBetweenDays(lastDate, cdate, true);
                if (days == 1) {
                    tempDates.add(cdate);
                } else {
                    tempDates = new ArrayList<Date>();
                    tempDates.add(cdate);
                    result.add(tempDates);
                }
            }

        }

        return result;
    }

    /**
     * 获取量过日期段之间的所有日期<br/>
     * 例如：getBetweenDates("2011-05-02", "2011-05-05")<br/>
     * 结果：2011-05-02, 2011-05-03, 2011-05-04, 2011-05-05
     *
     * @param fromDate 开始日期
     * @param toDate   结束日期
     * @return 日期段内的所有日期
     */
    public static List<java.sql.Date> getBetweenDates(java.sql.Date fromDate, java.sql.Date toDate) {
        List<java.sql.Date> result = new ArrayList<java.sql.Date>();
        // 如果开始日期大于结束日期交换
        if (toDate.getTime() < fromDate.getTime()) {
            java.sql.Date tempDate = fromDate;
            fromDate = toDate;
            toDate = tempDate;
        }

        Calendar ca = Calendar.getInstance();
        while (fromDate.getTime() <= toDate.getTime()) {
            ca.setTime(fromDate);
            java.sql.Date tempDate = new java.sql.Date(ca.getTime().getTime());
            result.add(tempDate);
            ca.add(Calendar.DATE, 1);
            fromDate = new java.sql.Date(ca.getTime().getTime());
        }

        return result;
    }

    /**
     * 从多个日期段中获取日期
     * <p>
     * <p/>
     * </p>
     *
     * @param dateList 多个日期段集合
     * @return
     */
    public static List<java.sql.Date> getAllDate(List<java.sql.Date[]> dateList) {
        List<java.sql.Date> result = new ArrayList<java.sql.Date>();
        for (Object[] objs : dateList) {
            if (objs.length < 2) {
                continue;
            }
            java.sql.Date date1 = (java.sql.Date) objs[0];
            java.sql.Date date2 = (java.sql.Date) objs[1];
            List<java.sql.Date> betweenDates = getBetweenDates(date1, date2);
            for (java.sql.Date date : betweenDates) {
                if (!result.contains(date)) {
                    result.add(date);
                }
            }
        }
        return result;
    }

    /**
     * 将出生日期与当前日期相减，获得年龄
     *
     * @param birthdayDate
     * @return
     */
    public static int getAge(Date birthdayDate) {
        String formatCurrent = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

        int firstCu = formatCurrent.indexOf("-");
        int lastCu = formatCurrent.lastIndexOf("-");
        String currentYearStr = formatCurrent.substring(0, firstCu);
        String currentMonthStr = formatCurrent.substring(firstCu + 1, lastCu);
        String currentDayStr = formatCurrent.substring(lastCu + 1);
        int currentYear = Integer.valueOf(currentYearStr);
        int currentMonth = Integer.valueOf(currentMonthStr);
        int currentDay = Integer.valueOf(currentDayStr);

        String formatBirthday = new SimpleDateFormat("yyyy-MM-dd").format(birthdayDate);

        int first = formatBirthday.indexOf("-");
        int last = formatBirthday.lastIndexOf("-");
        String birthYearStr = formatBirthday.substring(0, first);
        String birthMonthStr = formatBirthday.substring(first + 1, last);
        String birthDayStr = formatBirthday.substring(last + 1);

        int birthYear = Integer.valueOf(birthYearStr);
        int birthMonth = Integer.valueOf(birthMonthStr);
        int birthDay = Integer.valueOf(birthDayStr);

        if (currentMonth > birthMonth) {
            return currentYear - birthYear;
        } else if (currentMonth == birthMonth) {
            if (currentDay >= birthDay) {
                return currentYear - birthYear;
            } else {
                return currentYear - birthYear - 1;
            }
        } else {
            return currentYear - birthYear - 1;
        }
    }

    /**
     * 获取年月，例如 201009
     *
     * @param dateObj
     * @return
     */
    public static String getYearMonth(Date dateObj) {
        return format(dateObj, "yyyyMM");
    }

    /**
     * 根据指定年月计算上月年月标示<br/>
     *
     * @param yearMonth
     * @return 201010返回201009，201001返回200912
     */
    public static String getPreYearMonth(String yearMonth) {
        if (yearMonth.length() == 6) {
            int year = Integer.valueOf(yearMonth.substring(0, 4));
            int month = Integer.valueOf(yearMonth.substring(4));
            if (month != 1) {
                month -= 1;
            } else {
                year -= 1;
                month = 12;
            }
            return year + (month < 10 ? "0" + month : String.valueOf(month));
        }
        return "";
    }

    /**
     * @param date1 需要比较的时间 不能为空(null),需要正确的日期格式
     * @param date2 被比较的时间  为空(null)则为当前时间
     * @param type 返回值类型   0为多少天，1为多少个月，2为多少年
     * @return
     */
    public static int compareDate(Date date1, Date date2, int type) {
        int n = 0;

        String[] u = {"天", "月", "年"};

        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c1.setTime(date1);
        c2.setTime(date2);
        //List list = new ArrayList();
        while (!c1.after(c2)) {                     // 循环对比，直到相等，n 就是所要的结果
            //list.add(df.format(c1.getTime()));    // 这里可以把间隔的日期存到数组中 打印出来
            n++;
            if (type == 1) {
                c1.add(Calendar.MONTH, 1);          // 比较月份，月份+1
            } else {
                c1.add(Calendar.DATE, 1);           // 比较天数，日期+1
            }
        }

        n = n - 1;

        if (type == 2) {
            n = (int) n / 365;
        }
        return n;
    }

    /**
     * 用自然语言描述两个日期相差的时间
     * @param date1
     * @param date2
     * @return
     */
    public static String getDiffNaturalLang(Date date1, Date date2) {
        String result = "";
        int monthSpace = compareDate(date1, date2, 1);

        if (monthSpace >= 12) {
            result += (monthSpace / 12) + "年";
            if (monthSpace > 0 && monthSpace % 12 > 0) {
                result += (monthSpace % 12) + "个月";
            }
            return result;
        } else {
            return monthSpace + "个月";
        }
    }

    /**
     * 获取当前年份
     */
    public static Integer getCurrentYear() {
        Calendar ca = Calendar.getInstance();
        return ca.get(Calendar.YEAR);
    }

    /**
     * 获取当前月份
     */
    public static Integer getCurrentMonth() {
        Calendar ca = Calendar.getInstance();
        return ca.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取当前周在一年的周次
     */
    public static Integer getCurrentWeekOfYear() {
        Calendar ca = Calendar.getInstance();
        return ca.get(Calendar.WEEK_OF_YEAR) - 1;
    }

    /**
     * 获取当前周在当月的周次
     */
    public static Integer getCurrentWeekOfMonth() {
        Calendar ca = Calendar.getInstance();
        return ca.get(Calendar.WEEK_OF_MONTH);
    }

    /**
     * 从日历对象抽取java.sql.Date
     *
     * @param calendar
     * @return
     * @throws ParseException
     */
    public static java.sql.Date extractSqlDate(Calendar calendar) throws ParseException {
        if (calendar != null) {
            String s = calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) + 1) + "-"
                    + calendar.get(Calendar.DATE);
            return parseSqlDate(s, DateFormatType.FORMAT_DATE);
        }
        return null;
    }

}

/**
 * <p><b>Title：</b>日期大小比较</p>
 * <p><b>Description：</b>实现比较接口，按照排序类型[升序,降序]排列日期集合</p>
 *
 */
class DateComparator implements Comparator<Date> {

    int orderType;

    public DateComparator(int orderType) {
        this.orderType = orderType;
    }

    public int compare(Date d1, Date d2) {
        if (d1.getTime() > d2.getTime()) {
            if (orderType == DateUtil.DATE_ORDER_ASC) {
                return 1;
            } else {
                return -1;
            }
        } else {
            if (d1.getTime() == d2.getTime()) {
                return 0;
            } else {
                if (orderType == DateUtil.DATE_ORDER_DESC) {
                    return 1;
                } else {
                    return -1;
                }
            }
        }
    }

}